﻿using System.Reflection;
using BPM.Web.Shared.Dependencies;
using WebApiClientCore;

namespace Microsoft.Extensions.DependencyInjection;

/// <summary>
/// 依赖注入扩展方法
/// </summary>
public static class DependencyInjectionExtensions
{
    private static Type s_httpApi = typeof(IHttpApi);
    private static Type s_scoped = typeof(ScopedAttribute);
    private static Type s_singleton = typeof(SingletonAttribute);
    private static Type s_transient = typeof(TransientAttribute);

    /// <summary>
    /// 注册所有作用域、瞬态和单例属性修饰的类以及HttpApi代理Client。
    /// </summary>
    /// <param name="services"></param>
    /// <param name="configureOptions">配置HttpApiOptions的默认值，为空时不注册HttpApi</param>
    public static void AddDependencyInjection(this IServiceCollection services, Action<HttpApiOptions>? configureOptions = null)
    {
        if (services == null)
        {
            return;
        }

        var types = AppDomain.CurrentDomain.GetAssemblies().SelectMany(t => t.GetTypes());

        //对象可能依赖IHttpApi对象，所以先注册
        if (configureOptions != null)
        {
            foreach (var type in types.Where(x => x.IsInterface && x.IsAssignableTo(s_httpApi)))
            {
                services.AddHttpApi(type);
            }
            services.AddWebApiClient().ConfigureHttpApi(configureOptions);
        }

        //注册所有用作用域、瞬态和单例属性修饰的类。
        foreach (var type in types.Where(t => t.IsClass && t.Name[0] != '<' && !t.IsAbstract))
        {
            TryRegisterType(type, services);
        }
    }

    /// <summary>
    /// 注册对象
    /// </summary>
    /// <param name="type"></param>
    /// <param name="services"></param>
    private static void TryRegisterType(Type type, IServiceCollection services)
    {
        foreach (var customAttribute in type.CustomAttributes)
        {
            if (customAttribute.AttributeType == s_scoped)
            {
                RegisterScoped(services, type, customAttribute);
                return;
            }

            if (customAttribute.AttributeType == s_singleton)
            {
                RegisterSingleton(services, type, customAttribute);
                return;
            }

            if (customAttribute.AttributeType == s_transient)
            {
                RegisterTransient(services, type, customAttribute);
                return;
            }
        }
    }

    /// <summary>
    /// 注册作用域对象类型
    /// </summary>
    /// <param name="services"></param>
    /// <param name="injectable"></param>
    /// <param name="customAttributeData"></param>
    private static void RegisterScoped(IServiceCollection services, Type injectable, CustomAttributeData customAttributeData)
    {
        if (customAttributeData.ConstructorArguments.Count > 0)
        {
            var serviceType = customAttributeData.ConstructorArguments[0].Value as Type;
            if (serviceType != null)
            {
                services.AddScoped(serviceType, injectable);
            }
            else
            {
                services.AddScoped(injectable);
            }
        }
        else
        {
            services.AddScoped(injectable);
        }
    }

    /// <summary>
    /// 注册单例对象类型
    /// </summary>
    /// <param name="services"></param>
    /// <param name="injectable"></param>
    /// <param name="customAttributeData"></param>
    private static void RegisterSingleton(IServiceCollection services, Type injectable, CustomAttributeData customAttributeData)
    {
        if (customAttributeData.ConstructorArguments.Count > 0)
        {
            var serviceType = customAttributeData.ConstructorArguments[0].Value as Type;
            if (serviceType != null)
            {
                services.AddSingleton(serviceType, injectable);
            }
            else
            {
                services.AddSingleton(injectable);
            }
        }
        else
        {
            services.AddSingleton(injectable);
        }
    }

    /// <summary>
    /// 注册瞬时对象类型
    /// </summary>
    /// <param name="services"></param>
    /// <param name="injectable"></param>
    /// <param name="customAttributeData"></param>
    private static void RegisterTransient(IServiceCollection services, Type injectable, CustomAttributeData customAttributeData)
    {
        if (customAttributeData.ConstructorArguments.Count > 0)
        {
            var serviceType = customAttributeData.ConstructorArguments[0].Value as Type;
            if (serviceType != null)
            {
                services.AddTransient(serviceType, injectable);
            }
            else
            {
                services.AddTransient(injectable);
            }
        }
        else
        {
            services.AddTransient(injectable);
        }
    }
}
